package me.illtamer.infinite.file.parser;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.stream.JsonWriter;
import lombok.extern.slf4j.Slf4j;
import me.illtamer.infinite.config.Configuration;
import me.illtamer.infinite.file.FileType;
import me.illtamer.infinite.file.source.FileSource;
import me.illtamer.infinite.hook.TranslatorAdaptor;
import me.illtamer.infinite.pojo.Response;
import me.illtamer.infinite.pojo.TransObject;
import me.illtamer.infinite.util.JsonUtil;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Slf4j
@SuppressWarnings("unchecked")
public class JsonParser implements TypeParser {

    @Override
    public void parse(TranslatorAdaptor adaptor, FileSource source) throws IOException {
        LinkedTreeMap<String, Object> treeMap = new Gson().fromJson(source.getContent(), LinkedTreeMap.class);
        LinkedHashMap<String, String> record = new LinkedHashMap<>(treeMap.size());
        recursionToMap(null, record, treeMap);

        List<Map<String, String>> responseList = new ArrayList<>();

        long size = 0;
        LinkedHashMap<String, String> requestMap = new LinkedHashMap<>();

        List<LinkedHashMap<String, String>> translateMapList = new LinkedList<>();

        List<String> list = new LinkedList<>(record.keySet());
        for (int i = 0; i < list.size(); ++ i) {
            String key = list.get(i);
            // 检查是否存在下一级
            if (i != list.size()-1 && list.get(i+1).startsWith(key + ".")) continue;
            Object object = record.get(key);
//            if (!(object instanceof String)) continue;
            String value = ((String) object)
                    .replace("<", "=#").replace(">", "#=");

            if (size + value.length() > adaptor.getMaxLength()) {
                if (value.length() > adaptor.getMaxLength())
                    throw new IllegalArgumentException("Too long text length ! Limited: " + adaptor.getMaxLength() + " Found: " + value.length());
                translateMapList.add(requestMap);
                requestMap = new LinkedHashMap<>();
                requestMap.put(key, value);
                size = value.length();
            } else {
                requestMap.put(key, value);
                size += value.length();
            }
        }
        translateMapList.add(requestMap);
        for (LinkedHashMap<String, String> perRequest : translateMapList) {
            StringBuilder builder = new StringBuilder();
            for (String value : perRequest.values())
                builder.append(value).append("\n");
            String response = adaptor.translate(builder.toString(), "auto", "zh")
                    .replace("#=", ">").replace("=#", "<");

            TransObject object = JsonUtil.getObject(response);
            if (object.getResponse() != Response.SUCCESS) {
                log.warn("Failed translate {}", object);
                continue;
            }

            AtomicInteger i = new AtomicInteger();
            List<String> values = object.getTransPairs().stream().map(TransObject.TransPair::getDst).collect(Collectors.toList());
            Map<String, String> resultMap = perRequest.entrySet().stream().collect(Collectors.toMap(
                    Map.Entry::getKey,
                    entry -> values.get(i.getAndIncrement())
            ));

            responseList.add(resultMap);
        }

        // json to file
        File targetFile = new File(Configuration.TARGET_FOLDER, source.getName());
        if (!targetFile.createNewFile()) {
            log.warn("Can not create file({}), is it already exists ?", targetFile.getAbsolutePath());
        }

        JsonObject jsonObject = new JsonObject();
        try(JsonWriter writer = new JsonWriter(new FileWriter(targetFile))) {
            responseList.forEach(stringMap ->
                    stringMap.forEach((key, value) ->
                            recursionToJson(key, value, jsonObject)));
            Gson gson = new GsonBuilder().setPrettyPrinting().create();
            gson.toJson(jsonObject, writer);
        }
    }

    /**
     * 递归深度化 JsonObject
     * */
    public static void recursionToJson(String current, String value, JsonObject root) {
        int index = current.indexOf(".");
        if (index != -1) { // 嵌套
            String parent = current.substring(0, index);
            current = current.substring(index + 1);

            JsonObject object = (JsonObject) root.get(parent);
            if (object == null) {
                object = new JsonObject();
                root.add(parent, object);
            }
            recursionToJson(current, value, object);
        } else {
            root.add(current, new JsonPrimitive(value));
        }
    }

    /**
     * 递归将 JsonObject 去深度化
     * */
    private static void recursionToMap(String parent, LinkedHashMap<String, String> record, LinkedTreeMap<String, Object> map) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            String path = parent != null ? parent + "." + key : key;
            if (value instanceof LinkedTreeMap) {
                recursionToMap(path, record, ((LinkedTreeMap<String, Object>) value));
            }else if (value instanceof String) {
                record.put(path, (String) value);
            } else {
                log.warn("Unexpected element {}({})", value, value.getClass());
            }
        }
    }

    @Override
    public FileType parserType() {
        return FileType.JSON;
    }

}
